package com.xxx.generator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.util.*;

/**
 * 数据库表结构转Java实体类生成器
 * 支持根据数据库表结构自动生成带有Lombok和Swagger注解的Java实体类
 */
public class EntityGenerator {
    // 数据库连接配置
    private static final String URL = "jdbc:mysql://localhost:3306/blog";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root123";
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    // 生成文件配置
    private static final String OUTPUT_DIR = "E:\\develop\\p\\blog\\blog-pojo\\src\\main\\java\\org\\example\\blogpojo\\blog\\domain\\";
    private static final String PACKAGE_NAME = "org.example.blogpojo.blog.domain";
    private static final boolean USE_LOMBOK = true;
    private static final boolean USE_SWAGGER = true;

    public static void main(String[] args) {
        // 指定要生成实体类的表名，若为空则生成所有表,下面是可变参数,多张表之间以逗号隔开
        System.out.println("请输入你要生成的实体类的表名,多张表之间以逗号隔开,不输入直接enter回车生成所有的表的实体类");
        String tables = new Scanner(System.in).next();
        //List<String> tableNames = Arrays.asList("tb_article");//
        List<String> tableNames = Arrays.asList(tables);

        EntityGenerator generator = new EntityGenerator();
        try {
            if (tableNames.isEmpty()) {
                generator.generateAllEntities();
            } else {
                for (String tableName : tableNames) {
                    generator.generateEntity(tableName);
                }
            }
            System.out.println("实体类生成完成！");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成所有表的实体类
     */
    public void generateAllEntities() throws Exception {
        List<String> tableNames = getTableNames();
        for (String tableName : tableNames) {
            generateEntity(tableName);
        }
    }

    /**
     * 生成单个表的实体类
     */
    public void generateEntity(String tableName) throws Exception {
        List<Column> columns = getTableColumns(tableName);
        //String className = tableToClassName(tableName);//带有tb
        String className = processTableName(tableName);//不带tb
        String entityCode = buildEntityCode(tableName, className, columns);
        writeToFile(className, entityCode);
    }

    /**
     * 获取数据库中所有表名
     */
    private List<String> getTableNames() throws Exception {
        List<String> tableNames = new ArrayList<>();
        Connection conn = getConnection();
        DatabaseMetaData metaData = conn.getMetaData();
        ResultSet rs = metaData.getTables(null, null, null, new String[]{"TABLE"});
            
        while (rs.next()) {
            tableNames.add(rs.getString("TABLE_NAME"));
        }

        return tableNames;
    }

    /**
     * 获取表的列信息
     */
    private List<Column> getTableColumns(String tableName) throws Exception {
        List<Column> columns = new ArrayList<>();
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName + " LIMIT 1")) {
            
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            
            // 获取主键信息
            Set<String> primaryKeys = getPrimaryKeys(conn, tableName);
            
            // 获取列注释
            Map<String, String> columnComments = getColumnComments(conn, tableName);
            
            for (int i = 1; i <= columnCount; i++) {
                Column column = new Column();
                column.setName(metaData.getColumnName(i));
                column.setType(convertToJavaType(metaData.getColumnTypeName(i)));
                // 找到类型映射的逻辑部分
                //String javaType = mapDbTypeToJavaType(metaData.getColumnTypeName(i),metaData.getPrecision(i));

                column.setComment(columnComments.getOrDefault(column.getName(), ""));
                column.setPrimaryKey(primaryKeys.contains(column.getName()));
                columns.add(column);
            }
        }
        return columns;
    }

    /**
     * 获取表的主键信息
     */
    private Set<String> getPrimaryKeys(Connection conn, String tableName) throws SQLException {
        Set<String> primaryKeys = new HashSet<>();
        DatabaseMetaData metaData = conn.getMetaData();
        try (ResultSet rs = metaData.getPrimaryKeys(null, null, tableName)) {
            while (rs.next()) {
                primaryKeys.add(rs.getString("COLUMN_NAME"));
            }
        }
        return primaryKeys;
    }

    /**
     * 获取列注释
     */
    private Map<String, String> getColumnComments(Connection conn, String tableName) throws SQLException {
        Map<String, String> columnComments = new HashMap<>();
        try (Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SHOW FULL COLUMNS FROM " + tableName)) {
            
            while (rs.next()) {
                columnComments.put(rs.getString("Field"), rs.getString("Comment"));
            }
        }
        return columnComments;
    }

    /**
     * 数据库类型转换为Java类型
     */
    private String convertToJavaType(String sqlType) {
        sqlType = sqlType.toUpperCase();
        if (sqlType.equals("INT")) {
            return "Integer";
        } else if (sqlType.equals("BIGINT")) {
            return "Long";
        } else if (sqlType.equals("VARCHAR") || sqlType.equals("TEXT") || sqlType.equals("CHAR")) {
            return "String";
        } else if (sqlType.equals("DATETIME") || sqlType.equals("TIMESTAMP")) {
            return "Date";
        } else if (sqlType.equals("DECIMAL") || sqlType.equals("NUMERIC")) {
            return "BigDecimal";
        } else if (sqlType.equals("BOOLEAN")) {
            return "Boolean";
        }
        return "Object";
    }

    /**
     * 将数据库类型映射为Java类型
     * @param columnType 数据库列类型
     * @param columnSize 列大小
     * @return Java类型名称
     */
    private String mapDbTypeToJavaType(String columnType, int columnSize) {
        // 转为小写便于统一处理
        columnType = columnType.toLowerCase();

        // 处理常见的数值类型
        if (columnType.contains("int")) {
            if (columnType.contains("bigint")) {
                return "Long";  // bigint 对应 Long
            } else if (columnType.contains("tinyint") && columnSize == 1) {
                return "Boolean";  // 特殊处理 tinyint(1) 作为布尔值
            } else {
                return "Integer";  // 其他整数类型使用 Integer
            }
        } else if (columnType.contains("char") || columnType.contains("text")) {
            return "String";
        } else if (columnType.contains("date") || columnType.contains("time")) {
            return "Date";
        } else if (columnType.contains("decimal") || columnType.contains("numeric")) {
            return "BigDecimal";
        } else if (columnType.contains("float")) {
            return "Float";
        } else if (columnType.contains("double")) {
            return "Double";
        }

        // 默认返回 String
        return "String";
    }

    /**
     * 表名转类名（驼峰命名）
     */
    private String tableToClassName(String tableName) {
        if (tableName == null || tableName.isEmpty()) {
            return "";
        }
        
        StringBuilder className = new StringBuilder();
        String[] parts = tableName.split("_");
        
        for (String part : parts) {
            if (part.isEmpty()) continue;
            className.append(Character.toUpperCase(part.charAt(0)));
            className.append(part.substring(1).toLowerCase());
        }
        
        return className.toString();
    }

    /**
     * 列名转属性名（驼峰命名）
     */
    private String columnToPropertyName(String columnName) {
        if (columnName == null || columnName.isEmpty()) {
            return "";
        }
        
        StringBuilder propertyName = new StringBuilder();
        String[] parts = columnName.split("_");
        
        propertyName.append(parts[0].toLowerCase());
        
        for (int i = 1; i < parts.length; i++) {
            String part = parts[i];
            if (part.isEmpty()) continue;
            propertyName.append(Character.toUpperCase(part.charAt(0)));
            propertyName.append(part.substring(1).toLowerCase());
        }
        
        return propertyName.toString();
    }

    /**
     * 构建实体类代码ywj
     */
    private String buildEntityCode(String tableName, String className, List<Column> columns) {
        StringBuilder code = new StringBuilder();
        
        // 添加包声明
        code.append("package ").append(PACKAGE_NAME).append(";\n\n");
        
        // 添加必要的导入
        code.append("import java.io.Serializable;\n");
        if (columns.stream().anyMatch(c -> "Date".equals(c.getType()))) {
            code.append("import java.util.Date;\n");
        }
        if (columns.stream().anyMatch(c -> "BigDecimal".equals(c.getType()))) {
            code.append("import java.math.BigDecimal;\n");
        }
        if (USE_LOMBOK) {
            code.append("import lombok.Data;\n");
        }
        if (USE_SWAGGER) {
            code.append("import io.swagger.v3.oas.annotations.media.Schema;\n");
            //code.append("import io.swagger.annotations.ApiModelProperty;\n");
        }
        code.append("\n");
        
        // 添加类注释
        String tableComment = getTableComment(tableName);
        code.append("/**\n");
        code.append(" * ").append(tableComment).append("\n");
        code.append(" * @TableName ").append(tableName).append("\n");
        code.append(" */\n");
        
        // 添加类声明
        if (USE_LOMBOK) {
            code.append("@Data\n");
        }
        code.append("public class ").append(className).append(" implements Serializable {\n\n");
        
        // 添加序列化ID
        code.append("    private static final long serialVersionUID = 1L;\n\n");
        
        // 添加字段
        for (Column column : columns) {
            String propertyName = columnToPropertyName(column.getName());
            
            // 添加字段注释
            //code.append("    /**\n");
            //code.append("     * ").append(column.getComment()).append("\n");
            //code.append("     */\n");
            
            // 添加Swagger注解
            if (USE_SWAGGER) {
                code.append("    @Schema (description = \"").append(column.getComment()).append("\")\n");
            }
            
            // 添加字段声明,我做了修改,创建时间,修改时间赋值=new Date()
            //code.append("    private ").append(column.getType()).append(" ").append(propertyName).append(";\n\n");
            if ("createTime".equals(propertyName)||"updateTime".equals(propertyName)){
                code.append("    private ").append(column.getType()).append(" ").append(propertyName).append("=new Date()").append(";\n\n");
            }else {
                code.append("    private ").append(column.getType()).append(" ").append(propertyName).append(";\n\n");
            }
        }
        
        // 闭合类
        code.append("}");
        
        return code.toString();
    }

    /**
     * 获取表注释（这里简化处理，实际应用中可能需要查询数据库元数据）
     */
    private String getTableComment(String tableName) {
        // 简单处理，实际应用中应该从数据库元数据获取
        String[] parts = tableName.split("_");
        StringBuilder comment = new StringBuilder();
        
        for (String part : parts) {
            if (part.isEmpty()) continue;
            comment.append(part.substring(0, 1).toUpperCase());
            comment.append(part.substring(1).toLowerCase());
            comment.append(" ");
        }
        
        return comment.toString().trim() + "表";
    }

    /**
     * 将代码写入文件
     */
    private void writeToFile(String className, String code) throws IOException {
        File dir = new File(OUTPUT_DIR);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        
        File file = new File(OUTPUT_DIR + className + ".java");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            writer.write(code);
        }
    }

    /**
     * 获取数据库连接
     */
    private Connection getConnection() throws Exception {
        Class.forName(DRIVER);
        return DriverManager.getConnection(URL, USERNAME, PASSWORD);
    }

    /**
     * 列信息内部类
     */
    private static class Column {
        private String name;
        private String type;
        private String comment;
        private boolean primaryKey;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getComment() {
            return comment;
        }

        public void setComment(String comment) {
            this.comment = comment;
        }

        public boolean isPrimaryKey() {
            return primaryKey;
        }

        public void setPrimaryKey(boolean primaryKey) {
            this.primaryKey = primaryKey;
        }
    }

    /**
     * 移除表名前缀并转换为类名
     * @param tableName 表名
     * @return 处理后的类名
     */
    private String processTableName(String tableName) {
        // 定义要移除的前缀数组，可以根据需要添加更多
        String[] prefixes = {"tb_", "t_", "sys_"};

        // 移除匹配的前缀
        for (String prefix : prefixes) {
            if (tableName.toLowerCase().startsWith(prefix)) {
                tableName = tableName.substring(prefix.length());
                break;
            }
        }

        // 将下划线命名转为驼峰命名
        return toCamelCase(tableName);
    }

    /**
     * 将下划线命名转换为驼峰命名
     */
    private String toCamelCase(String input) {
        StringBuilder result = new StringBuilder();
        boolean capitalizeNext = false;

        for (char c : input.toCharArray()) {
            if (c == '_') {
                capitalizeNext = true;
            } else {
                if (capitalizeNext) {
                    result.append(Character.toUpperCase(c));
                    capitalizeNext = false;
                } else {
                    result.append(Character.toLowerCase(c));
                }
            }
        }

        // 首字母大写
        if (result.length() > 0) {
            result.setCharAt(0, Character.toUpperCase(result.charAt(0)));
        }

        return result.toString();
    }
}    